#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/error.h>

// 读取文件内容
int read_file(const char *filename, uint8_t **content, size_t *len) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        printf("错误: 无法打开文件 %s\n", filename);
        return 0;
    }
    
    fseek(file, 0, SEEK_END);
    *len = ftell(file);
    fseek(file, 0, SEEK_SET);
    
    *content = (uint8_t *)malloc(*len + 1);
    if (!*content) {
        printf("错误: 内存分配失败\n");
        fclose(file);
        return 0;
    }
    
    size_t read_len = fread(*content, 1, *len, file);
    fclose(file);
    
    if (read_len != *len) {
        printf("错误: 读取文件不完整\n");
        free(*content);
        return 0;
    }
    
    (*content)[*len] = '\0'; // 添加字符串结束符
    return 1;
}

// 写入文件内容
int write_file(const char *filename, const uint8_t *content, size_t len) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        printf("错误: 无法创建文件 %s\n", filename);
        return 0;
    }
    
    size_t write_len = fwrite(content, 1, len, file);
    fclose(file);
    
    if (write_len != len) {
        printf("错误: 写入文件不完整\n");
        return 0;
    }
    
    return 1;
}

// 从PEM文件加载公钥
int load_public_key(const char *filename, SM2_KEY *key) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        printf("错误: 无法打开公钥文件 %s\n", filename);
        return 0;
    }
    
    if (sm2_public_key_info_from_pem(key, file) != 1) {
        printf("错误: 加载公钥失败\n");
        fclose(file);
        return 0;
    }
    
    fclose(file);
    return 1;
}

// 从PEM文件加载私钥（带密码）
int load_private_key(const char *filename, const char *password, SM2_KEY *key) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        printf("错误: 无法打开私钥文件 %s\n", filename);
        return 0;
    }
    
    if (sm2_private_key_info_decrypt_from_pem(key, password, file) != 1) {
        printf("错误: 加载私钥失败，请检查密码\n");
        fclose(file);
        return 0;
    }
    
    fclose(file);
    return 1;
}

// 打印十六进制数据
void print_hex(const char *label, const uint8_t *data, size_t len) {
    printf("%s (%zu字节):\n", label, len);
    for (size_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
        if ((i + 1) % 32 == 0) printf("\n");
        else if ((i + 1) % 8 == 0) printf(" ");
    }
    printf("\n\n");
}

int main() {
    printf("=== SM2签名验签 ===\n\n");
    
    uint8_t *message = NULL;
    size_t message_len = 0;
    uint8_t dgst[SM3_DIGEST_SIZE];
    uint8_t signature[SM2_MAX_SIGNATURE_SIZE];
    size_t siglen = 0;
    
    SM2_KEY public_key;
    SM2_KEY private_key;
    const char *password = "1318"; // 私钥密码
    
    // 1. 读取要签名的文件
    printf("1. 读取文件 sn.txt\n");
    if (!read_file("sn.txt", &message, &message_len)) {
        return 1;
    }
    
    printf("文件内容(%zu字节):\n", message_len);
    printf("%.*s\n\n", (int)message_len, message);
    
    // 2. 计算SM3哈希
    printf("2. 计算SM3哈希值\n");
    {
        SM3_CTX ctx;
        sm3_init(&ctx);
        sm3_update(&ctx, message, message_len);
        sm3_finish(&ctx, dgst);
    }
    print_hex("SM3哈希结果", dgst, SM3_DIGEST_SIZE);
    // 3. 加载私钥
    printf("3. 加载私钥（密码: %s）\n", password);
    if (!load_private_key("sm2.pem", password, &private_key)) {
        free(message);
        return 1;
    }
    printf("✓ 私钥加载成功\n");
    
    // 4. 生成签名
    printf("4. 生成数字签名\n");
    if (sm2_sign(&private_key, dgst, signature, &siglen) != 1) {
        printf("错误: 签名失败\n");
        free(message);
        return 1;
    }
    
    if (!write_file("sn_signature.bin", signature, siglen)) {
        free(message);
        return 1;
    }
    printf("签名成功\n");
    print_hex("签名内容", signature, siglen);
    
    // 5. 加载公钥
    printf("5. 加载公钥\n");
    if (!load_public_key("sm2pub.pem", &public_key)) {
        free(message);
        return 1;
    }
    printf("公钥加载成功\n");
    
    // 6. 验证签名
    printf("6. 验证数字签名\n");
    int verify_result = sm2_verify(&public_key, dgst, signature, siglen);
    
    if (verify_result == 1) {
        printf("签名验证成功！\n\n");
    } else {
        printf("签名验证失败\n\n");
    }
    
    // 7. 测试篡改检测
    printf("7. 测试篡改检测\n");
    
    // 创建被篡改的消息
    uint8_t *tampered_message = (uint8_t *)malloc(message_len + 10);
    if (!tampered_message) {
        printf("错误: 内存分配失败\n");
        free(message);
        return 1;
    }
    
    memcpy(tampered_message, message, message_len);
    memcpy(tampered_message + message_len, "tampered", 9);
    size_t tampered_len = message_len + 8;
    
    // 计算被篡改消息的哈希
    uint8_t tampered_dgst[SM3_DIGEST_SIZE];
    {
        SM3_CTX ctx;
        sm3_init(&ctx);
        sm3_update(&ctx, tampered_message, tampered_len);
        sm3_finish(&ctx, tampered_dgst);
    }
    
    printf("篡改后的文件哈希:\n");
    print_hex("篡改后SM3哈希", tampered_dgst, SM3_DIGEST_SIZE);
    
    // 验证被篡改消息的签名
    int tampered_verify = sm2_verify(&public_key, tampered_dgst, signature, siglen);
    
    if (tampered_verify != 1) {
        printf("篡改检测成功 - 系统正确检测到文件被修改\n");
    } else {
        printf("篡改检测失败\n");
    }
    
    free(message);
    free(tampered_message);
    return 0;
}
